module main

import os
import flag
import strings

const (
	tool_version     = '0.0.4'
	tool_description = 'Converts a list of arbitrary files into a single v module file.'
)

struct Context {
mut:
	files       []string
	prefix      string
	show_help   bool
	module_name string
	write_file  string
}

fn (context Context) header() string {
	mut header_s := ''
	header_s += 'module $context.module_name\n'
	header_s += '\n'
	allfiles := context.files.join(' ')
	mut options := []string{}
	if context.prefix.len > 0 {
		options << '-p $context.prefix'
	}
	if context.module_name.len > 0 {
		options << '-m $context.module_name'
	}
	if context.write_file.len > 0 {
		options << '-w $context.write_file'
	}
	soptions := options.join(' ')
	header_s += '// File generated by:\n'
	header_s += '// v bin2v $allfiles $soptions\n'
	header_s += '// Please, do not edit this file.\n'
	header_s += '// Your changes may be overwritten.\n'
	header_s += 'const (\n'
	return header_s
}

fn (context Context) footer() string {
	return ')\n'
}

fn (context Context) file2v(bname string, fbytes []byte, bn_max int) string {
	mut sb := strings.new_builder(1000)
	bn_diff_len := bn_max - bname.len
	sb.write('\t${bname}_len' + ' '.repeat(bn_diff_len - 4) + ' = $fbytes.len\n')
	mut last_len := sb.len
	fbyte := fbytes[0]
	sb.write('\t$bname' + ' '.repeat(bn_diff_len) + ' = [byte($fbyte), ')
	for i := 1; i < fbytes.len; i++ {
		b := int(fbytes[i]).str()
		sb_diff_len := sb.len - last_len
		if i < 30 && sb_diff_len > 86 {
			sb.write('$b,\n\t\t')
			last_len = sb.len
		} else if sb_diff_len > 88 && 92 - sb_diff_len < b.len {
			sb.write('$b,\n\t\t')
			last_len = sb.len
		} else if i == fbytes.len - 1 {
			sb.write(b)
		} else {
			sb.write('$b, ')
		}
	}
	sb.write(']!!\n')
	return sb.str()
}

fn (context Context) bname_and_bytes(file string) ?(string, []byte) {
	fname := os.file_name(file)
	fname_no_dots := fname.replace('.', '_')
	byte_name := '$context.prefix$fname_no_dots'.to_lower()
	fbytes := os.read_bytes(file) or {
		return error('Error: $err')
	}
	return byte_name, fbytes
}

fn (context Context) max_bname_len(bnames []string) int {
	mut max := 0
	for n in bnames {
		if n.len > max {
			max = n.len
		}
	}
	// Add 4 to max due to "_len" suffix
	return max + 4
}

fn main() {
	mut context := Context{}
	mut fp := flag.new_flag_parser(os.args[1..])
	fp.application('v bin2v')
	fp.version(tool_version)
	fp.description(tool_description)
	fp.arguments_description('FILE [FILE]...')
	context.show_help = fp.bool('help', `h`, false, 'Show this help screen.')
	context.module_name = fp.string('module', `m`, 'binary', 'Name of the generated module.')
	context.prefix = fp.string('prefix', `p`, '', 'A prefix put before each resource name.')
	context.write_file = fp.string('write', `w`, '', 'Write directly to a file with the given name.')
	if context.show_help {
		println(fp.usage())
		exit(0)
	}
	files := fp.finalize() or {
		eprintln('Error: ' + err)
		exit(1)
	}
	real_files := files.filter(it != 'bin2v')
	if real_files.len == 0 {
		println(fp.usage())
		exit(0)
	}
	context.files = real_files
	if context.write_file != '' && !context.write_file.ends_with('.v') {
		context.write_file += '.v'
	}
	mut file_byte_map := map[string][]byte{}
	for file in real_files {
		bname, fbytes := context.bname_and_bytes(file) or {
			eprintln(err)
			continue
		}
		file_byte_map[bname] = fbytes
	}
	max_bname := context.max_bname_len(file_byte_map.keys())
	if context.write_file.len > 0 {
		mut out_file := os.create(context.write_file) or {
			panic(err)
		}
		out_file.write_str(context.header())
		for bname, fbytes in file_byte_map {
			out_file.write_str(context.file2v(bname, fbytes, max_bname))
		}
		out_file.write_str(context.footer())
	} else {
		println(context.header())
		for bname, fbytes in file_byte_map {
			println(context.file2v(bname, fbytes, max_bname))
		}
		println(context.footer())
	}
}
